package ranks;

import java.io.IOException;
import java.util.Set;

import org.apache.hadoop.mapred.Reporter;

import ranks.base.Const;
import ranks.base.Data;
import ranks.base.Util;
import ranks.base.ValueMerger;
import ranks.base.ValueType;

public class Customize {

	public static enum DataType {
		OUT_DEGREE(ValueType.LONG, ValueMerger.MAX), PAGE_RANK(
				ValueType.DOUBLE, ValueMerger.ADD), TRUST_RANK(
				ValueType.DOUBLE, ValueMerger.ADD), BIAS(ValueType.DOUBLE,
				ValueMerger.MAX);

		ValueType type;

		ValueMerger merger;

		DataType(ValueType type, ValueMerger merger) {
			this.type = type;
			this.merger = merger;
		}

		public String mergeValue(String prev, String cur) {
			return type.merge(merger, prev, cur);
		}

		@SuppressWarnings("unchecked")
		public <T> T parse(String s) {
			return (T) type.parse(s);
		}

	}

	public static Data initialData(String url, long outDegree,
			Set<String> seeds, double times) {
		Data ret = new Data();
		ret.set(DataType.PAGE_RANK, 1);
		ret.set(DataType.TRUST_RANK, 1);
		if (seeds.contains(Util.getHost(url))) {
			ret.set(DataType.BIAS, times);
		} else {
			ret.set(DataType.BIAS, 0);
		}
		ret.set(DataType.OUT_DEGREE, outDegree);
		return ret;
	}

	public static String readable(Data rank, long websize) {
		Double pagerank = rank.getValue(DataType.PAGE_RANK);
		Double trustrank = rank.getValue(DataType.TRUST_RANK);

		return (pagerank / websize) + "\t" + (trustrank / websize);
	}

	public static Data send(String source, Data rank, String target, long weight)
			throws IOException {
		Data ret = new Data();
		Long outDegree = rank.getValue(DataType.OUT_DEGREE);

		Double pagerank = rank.getValue(DataType.PAGE_RANK);
		ret.set(DataType.PAGE_RANK, pagerank * weight / outDegree);

		Double trustrank = rank.getValue(DataType.TRUST_RANK);
		ret.set(DataType.TRUST_RANK, trustrank * weight / outDegree);
		ret.set(DataType.BIAS, 0);

		ret.set(DataType.OUT_DEGREE, 0);
		
		return ret;
	}

	public static void delta(Data cur, Data old, Reporter reporter) {
		delta(cur, old, DataType.PAGE_RANK, reporter);
		delta(cur, old, DataType.TRUST_RANK, reporter);
	}

	public static Data fomular(Data data) {
		Data ret = new Data();

		Double pagerank = data.getValue(DataType.PAGE_RANK);
		ret.set(DataType.PAGE_RANK, pagerank * 0.85 + 0.15);

		Double trustrank = data.getValue(DataType.TRUST_RANK);
		Double bias = data.getValue(DataType.BIAS);
		ret.set(DataType.TRUST_RANK, trustrank * 0.85 + 0.15 * bias);

		ret.set(DataType.BIAS, data.get(DataType.BIAS));

		ret.set(DataType.OUT_DEGREE, data.get(DataType.OUT_DEGREE));

		return ret;
	}

	public static Data fomularZ(Data z, long websize) {
		Data ret = new Data();

		Double pagerank = z.getValue(DataType.PAGE_RANK);
		ret.set(DataType.PAGE_RANK, pagerank * 0.85 / websize);

		Double trustrank = z.getValue(DataType.TRUST_RANK);
		ret.set(DataType.TRUST_RANK, trustrank * 0.85 / websize);

		ret.set(DataType.OUT_DEGREE, 0);
		return ret;
	}

	private static void delta(Data cur, Data old, DataType type,
			Reporter reporter) {
		Double c = cur.getValue(type);
		Double o = old.getValue(type);
		double delta = Math.abs(c - o);
		if (delta >= Const.DELTA) {
			reporter.incrCounter("Delta", type.toString(), 1);
			long d = (long) Math.round(delta / Const.DELTA);
			reporter.incrCounter("Distance", type.toString(), d);
		}
	}

}
